How to boot Linux using UEFI with Secure Boot ?

Preview

In this tutorial, we will describe every steps to secure the boot of Linux on a PC. We will learn to backup existing certificates from the UEFI and write our own certificates. Moreover, you will also see how to sign our grub bootloader, kernel and initrd. Then, you will be able to configure the grub configuration file in order to only load signed kernel and initrd. Finally, we will protect the grub configuration file.

Here, I will be using a Centos 7 distribution, if you are using another check the documentation of your distribution for any problem or further information.

References

Contents

  • Check Secure Boot status.
  • Backup existing certificates from the UEFI.
  • Write your own certificates.
  • Sign your own grub / kernel / initrd.
  • Configure your grub.cfg to only load signed kernel and initrd.
  • Add a password protection to GRUB2.
  • Annexes :
    • A) Install sbsigntools and efitools on CentOS.

Prerequisite

  • A Linux distribution installed and booting with UEFI on a PC.
  • Disable Secure Boot in your BIOS.

You can also use a Virtual Environment following this tutorial.

Dependencies

  • Install sbsigntools.
  • Install efitools.

If you are using Centos 7 distribution, check how to install them in Annexe A.

Check Secure Boot status

To verify if your computer has booted with Secure Boot, type the following command :

$ bootctl status

In order to start this tutorial you need to have it disabled. If it is not go to your BIOS and disable it.

Backup existing certificates from the UEFI

In this section, we will backup the public keys / certificates from the UEFI keystore :

  • The Platform Key (PK) is a variable where the public key given by the hardware vendor (ex : Dell, HP, Asus, ...) is stored.
  • The Key Exchange Key (KEK) is a variable which can contains one or more public keys. Usually, Microsoft store his public key here.
  • The Database (db) is a variable containing a signature database commonly filled with public keys. It is used as a boot executable whitelist.
  • The Database Blacklist (dbx) is a variable similar to the db, but it contains the boot executable blacklist, also called "revoked keys/signatures".

Thoose variables are used to control the Secure Boot process.

  1. Prepare the backup directory and give it only root access :

    $ mkdir -p secu-os/backup
    $ chmod -v 700 secu-os/
    $ cd secu-os/backup/
    
  2. Backup actual efi keys :

    $ efi-readvar -v PK -o old_PK.esl
    $ efi-readvar -v KEK -o old_KEK.esl
    $ efi-readvar -v db -o old_db.esl
    $ efi-readvar -v dbx -o old_dbx.esl
    

    Warning : don't forget the -o option or your variable will be dumped as text and you wont be able to reload them.

Write your own certificates

Now we will create a new PK, KEK and kernel-signing keypair (db) with openssl.

Keys caracteristics (source) :

  • X.509 certificate format for the public key.
  • RSA asymmetric cryptosystem, with a 2048 bit key length.
  • Have 10 years (3650 days) to run until expiry.
  • Use SHA-256 as the public key's message digest.
  1. Prepare the efikeys directory :

    $ cd secu-os/
    $ mkdir efikeys
    $ cd efikeys/
    
  2. Create the keys with openssl :

    $ openssl req -new -x509 -newkey rsa:2048 -subj "/CN=tutorial's platform key/" -keyout PK.key -out PK.crt -days 3650 -nodes -sha256
    $ openssl req -new -x509 -newkey rsa:2048 -subj "/CN=tutorial's key-exchange-key/" -keyout KEK.key -out KEK.crt -days 3650 -nodes -sha256
    $ openssl req -new -x509 -newkey rsa:2048 -subj "/CN=tutorial's kernel-signing key/" -keyout db.key -out db.crt -days 3650 -nodes -sha256
    

    Each commands provide a private key and a certificates.

  3. Make the private key readable only by root :

    $ chmod -v 400 *.key
    

    Work In Progress : In order, to more protect the private keys, we are looking at encrypted file systems.

Sign your own grub / kernel / initrd

Update UEFI keystore with our own keys

Before signing our grub, kernel or initrd, we need to update the UEFI keystore variables with the keys that we have just created.

Some informations about keys format (source):

  • .key PEM format private keys for EFI binary and EFI signature list signing.
  • .crt PEM format certificates for sbsign.
  • .cer DER format certificates for firmware.
  • .esl Certificates in EFI Signature List for KeyTool and/or firmware.
  • .auth Certificates in EFI Signature List with authentication header (i.e. a signed certificate update file) for KeyTool and/or firmware.
  1. Create a GUID for owner identification :

    $ cd secu-os/efikeys/
    $ uuidgen --random > GUID.txt
    
  2. Prepare PK for installation in EFI :

    $ cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl
    $ sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth
    

    The first command convert a certificate into a EFI signature list.

    The second one sign the EFI signature list. In this case, we sign it with the PK private key.

  3. Prepare KEK for installation in EFI :

    $ cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl
    $ sign-efi-sig-list -g "$(< GUID.txt)" -a -k PK.key -c PK.crt KEK KEK.esl KEK.auth
    

    We add -a option to append to the file instead of replace it.

    We use PK private key to sign the signature list.

  4. Prepare db for installation in EFI :

    $ cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl
    $ sign-efi-sig-list -g "$(< GUID.txt)" -a -k KEK.key -c KEK.crt db db.esl db.auth
    

    Same, we use here -a option.

    We use KEK private key to sign the signature list.

  5. Prepare dbx for installation in EFI :

    $ sign-efi-sig-list -k KEK.key -c KEK.crt dbx ../backup/old_dbx.esl old_dbx.auth
    

    For the dbx, we just restore the old one and sign it with the KEK private key.

  6. Crete DER version of our 3 public keys (certificates) :

    $ openssl x509 -outform DER -in PK.crt -out PK.cer
    $ openssl x509 -outform DER -in KEK.crt -out KEK.cer
    $ openssl x509 -outform DER -in db.crt -out db.cer
    
  7. For dual boot, compound (old+new) in order to save microsoft signatures :

    $ cat old_KEK.esl KEK.esl > compound_KEK.esl
    $ cat old_db.esl db.esl > compound_db.esl
    $ sign-efi-sig-list -k PK.key -c PK.crt KEK compound_KEK.esl compound_KEK.auth
    $ sign-efi-sig-list -k KEK.key -c KEK.crt db compound_db.esl compound_db.auth
    

Entering setup mode (clearing keystore)

  1. To enter in setup mode, first rebbot the machine :

    $ reboot
    
  2. Then, enter in your BIOS and perform :

    • Clear UEFI secure boot variable
    • Save changes
    • Restart your machine

Note : in some computers you will have to enable custom keys in your BIOS.

Installing new keys into the keystore

  1. Verify that the secure boot variables are cleared :

    $ efi-readvar
    

    Expected output :

    Variable PK has no entries
    Variable KEK has no entries
    Variable db has no entries
    Variable dbx has no entries
    
  2. Return to your efikeys directory and update keystore with your own signatures :

    $ cd secu-os/efikeys/
    $ efi-updatevar -e -f ../backup/old_dbx.esl dbx
    $ efi-updatevar -e -f db.esl db
    $ efi-updatevar -e -f KEK.esl KEK
    $ efi-updatevar -f PK.esl PK
    

    The -e option specifies that an EFI signature list file have to be loaded.

    The -f option precise the filename.

    Note : for dual boot user replace db.esl, KEK.esl by compound_db.esl, compound_KEK.esl.

  3. Backup your installed certificates :

    $ cd ../backup/
    $ efi-readvar -v PK -o new_PK.esl
    $ efi-readvar -v KEK -o new_KEK.esl
    $ efi-readvar -v db -o new_db.esl
    $ efi-readvar -v dbx -o new_dbx.esl
    
  4. Verify the content of installed variables :

    $ efi-readvar
    

Sign your grub bootloader

  1. Backup your grub bootloader :

    $ su root
    $ cd secu-os/backup/
    $ cp /boot/efi/EFI/centos/grubx64.efi grubx64.efi.bak
    
  2. Sign the grub bootloader :

    $ cd secu-os/efikeys/
    $ sbsign --key db.key --cert db.crt --output /boot/efi/EFI/centos/grubx64.efi /boot/efi/EFI/centos/grubx64.efi 
    

    We use

Sign your kernel

  1. Backup your kernel :

    $ su root
    $ cd secu-os/backup/
    $ cp /boot/vmlinuz-xxx vmlinuz-xxx.bak
    
  2. Sign the kernel :

    $ cd secu-os/efikeys/
    $ sbsign --key db.key --cert db.crt --output /boot/vmlinuz-xxx /boot/vmlinuz-xxx
    

Sign your initrd

  1. Backup your initrd :

    $ su root
    $ cd secu-os/backup/
    $ cp /boot/initramfs-xxx initramfs-xxx.bak
    
  2. At the point, sbsigntools don't provide the possibility to sign the initrd. So we will use an other tool named gpg :

    Work In Progress : miss some steps to generate GPG.key.

    $ gpg --default-key GPG.key --detach-sign /boot/initramfs-xxx.img
    

GRUB only load signed kernel

  1. Backup your grub.cfg :

    $ su root
    $ cd secu-os/backup/
    $ cp /boot/efi/EFI/centos/grub.cfg grub.cfg.bak
    
  2. Edit your grub.cfg and add this at the top of the file :

    set check_signatures=enfore
    export check_signatures
    

Add a password protection to GRUB

To install password to "add" a little security on Grub2, you need to execute this command to set a new password :

$ grub2-setpassword

After that, in the "grub.cfg" file, you have to prevent "bypass" of password by removing all arguments "--unrestricted".

It has at least two ways to bypass Grub2 password for use of unsigned load :

  • Accessing the grub.cfg file by UEFI Shell and add argument/option "--unrestricted" will disable password protection.
  • Or delete the file "user.cfg" (on the same directory of "grub.cfg" by accessing UEFI Shell.

Warning : you need to be aware that all manual modification (like manual password configuration or signatures check) in grub.cfg will be erase by an update of grub for example with this command :

$ grub2-mkconfig -o /boot/grub2/grub.cfg

I advise you to do all modification like setting password directly in one of the configuration files used to build "grub.cfg". These files can be found in /etc/grub.d.

For example, you can set in file "01_users" :

#!/bin/sh -e
cat << EOF
​    set superusers="root"
​    password_pbkdf2 root "Your SHA password here"
​    set check-signatures = enforce
EOF

Like that, each time your "grub.cfg" will be regenerate, your changes will always be present to the grub configuration

Testing phase

Work In Progress

Conclusion

If you think that with the features we have done you have completly secure the boot process, then you are wrong. We just add some difficulties to break it and we will see in other articles ahow to add more protection by for example protect the grub.cfg file.

Annexes

A) Install sbsigntools and efitools

  • Create a work directory and install repositories in it :

    $ mkdir -p secu-os/install
    

    Use git clone or wget to download dependencies :

    $ cd secu-os/install/
    $ git clone https://git.kernel.org/pub/scm/linux/kernel/git/jejb/sbsigntools.git
    $ git clone https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git
    

    Proceed installations :

    $ cd sbsigntools/
    $ ./autogen.sh
    $ ./configure
    $ make check
    $ make install
    $ make installcheck
    $ cd ../efitools/
    $ make
    $ make install
    

    Note : you will certainly need to install some other packages, check your errors during compilation.

    Help : If you have troubles during the installation of efitools, try the version 1.8.0.